/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frbase64.c                                         */
/*  NOTE      = base64加密                                         */
/*  DATE      = 2015/04/14                                         */
/*******************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "frcomm.h"
#include "frmem.h"

// base64 tables    
static char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";    
static signed char index_64[128] = {    
        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,    
        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,    
        -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,    
        52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,    
        -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,    
        15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,    
        -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,    
        41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1    
} ;
#define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])

char *frbase64_encode(unsigned char *p_src, int i_src_len)
{
    unsigned char oval = 0;
    char *result = NULL;
    char *out = NULL;
    
    if (p_src == NULL || i_src_len <= 0)
        return NULL;
    
    result = (char *)frmalloc((i_src_len * 4) / 3 + 5);
    if (result == NULL)
        return NULL;
    out = result;
    
    while (i_src_len >= 3)
    {
        *out++ = basis_64[p_src[0] >> 2];
        *out++ = basis_64[((p_src[0] << 4) & 0x30) | (p_src[1] >> 4)];
        *out++ = basis_64[((p_src[1] << 2) & 0x3C) | (p_src[2] >> 6)];
        *out++ = basis_64[p_src[2] & 0x3F];
        p_src += 3;
        i_src_len -= 3;
    }
    if (i_src_len > 0)
    {
        *out++ = basis_64[p_src[0] >> 2];
        oval = (p_src[0] << 4) & 0x30 ;
        if (i_src_len > 1) oval |= p_src[1] >> 4;
        *out++ = basis_64[oval];
        *out++ = (i_src_len < 2) ? '=' : basis_64[(p_src[1] << 2) & 0x3C];
        *out++ = '=';
    }
    *out = '\0';    
    return result;
}    

unsigned char *frbase64_decode(const char *p_src, int *p_i_out)
{
    int c1, c2, c3, c4;
    int i_src_len = 0;
    unsigned char *result =NULL;
    unsigned char *out = NULL;
    *p_i_out = 0;
    
    if (p_src == NULL)
        return NULL;
    
    i_src_len = strlen(p_src);
    result =(unsigned char *)frmalloc((i_src_len * 3) / 4 + 1);
    if (result == NULL)
        return NULL;
    out = result;
    
    while (1)
    {    
        if (p_src[0]==0)
        {
            *out = '\0' ;
            return result;
        }
        c1 = p_src[0];
        if (CHAR64(c1) == -1)
            goto base64_decode_error;
        c2 = p_src[1];
        if (CHAR64(c2) == -1)
            goto base64_decode_error;
        c3 = p_src[2];
        if ((c3 != '=') && (CHAR64(c3) == -1))
            goto base64_decode_error;
        c4 = p_src[3];
        if ((c4 != '=') && (CHAR64(c4) == -1))
            goto base64_decode_error;
         p_src += 4;
        *out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
        *p_i_out += 1;
        if (c3 != '=')
        {
            *out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
            *p_i_out += 1;
            if (c4 != '=')
            {
                *out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
                    *p_i_out += 1;
            }
        }
    }
base64_decode_error:
    frfree(result);
    *p_i_out = 0;
    return NULL;
}
